#include"fs.h"
#include"stdio_kernal.h"
#include"inode.h"
#include"dir.h"
#include"super_block.h"
#include"memory.h"
#include"string.h"
#include"ide.h"
#include"debug.h"

#define OBR_SEC_CNT 1
#define SUPER_BLOCK_CNT 1


extern struct channel ata_channels[2];
extern list partition_list;
//the kernal current 
struct partition* curr_part;

void init_filesys(void){
	partition_format(&ata_channels[0].devices[1].primary_part[0],false);
	mount_partition("sdb1");
}

static void _partition_format(struct partition* p){
	struct super_block sb;
	uint32_t free_block_cnt;
	uint32_t block_bitmap_sec;
	uint32_t free_block_bits_cnt;

	memset(&sb,0,SECTOR_SIZE);
	sb.magic=FS_MAGIC;
	sb.sec_cnt=p->sec_cnt;
	sb.inode_bitmap_sec=div_round_up(MAX_FILE_PER_PART,BITS_PER_SEC);
	sb.inode_table_sec=div_round_up(sizeof(struct inode)*MAX_FILE_PER_PART,SECTOR_SIZE);
	sb.start_lba=p->start_lba;
	free_block_cnt=sb.sec_cnt-OBR_SEC_CNT-SUPER_BLOCK_CNT-sb.inode_bitmap_sec-sb.inode_table_sec;
	block_bitmap_sec=div_round_up(free_block_cnt,BITS_PER_SEC);

	free_block_bits_cnt=free_block_cnt-block_bitmap_sec;
	block_bitmap_sec=div_round_up(free_block_bits_cnt,BITS_PER_SEC);
	
	sb.block_bitmap_lba=sb.start_lba+OBR_SEC_CNT+SUPER_BLOCK_CNT;
	sb.block_bitmap_sec=block_bitmap_sec;

	sb.inode_bitmap_lba=sb.block_bitmap_lba+sb.block_bitmap_sec;
	sb.inode_table_lba=sb.inode_bitmap_lba+sb.inode_bitmap_sec;
	
	sb.free_block_lba=sb.inode_table_lba+sb.inode_table_sec;
	sb.root_ino=0;
	sb.dir_entry_size=sizeof(struct dir_entry);
	sb.pure_inode_size=sizeof(struct inode);
	//write super block info to partition sector
	ide_write(p->disk,(char*)&sb,sb.start_lba+1,1);

	printk("%s super block info sec_cnt 0x%x start_lba 0x%x \nblock_bitmap_lba 0x%x block_bitmap_sec 0x%x \ninode_bitmap_lba 0x%x inode_bitmap_sec 0x%x \ninode_table_lba 0x%x inode_table_sec 0x%x\nfree_block_lba 0x%x free_block_bits_cnt 0x%x\n",
	       	p->name,sb.sec_cnt,sb.start_lba, \
		sb.block_bitmap_lba,sb.block_bitmap_sec, \
		sb.inode_bitmap_lba,sb.inode_bitmap_sec, \
		sb.inode_table_lba,sb.inode_table_sec, \
		sb.free_block_lba,free_block_bits_cnt);
	uint32_t last_bytes;
	uint32_t last_bits;
	uint32_t last_size;
	uint32_t bits_index=0;
	
	char* buffer=sys_malloc(sb.block_bitmap_sec*SECTOR_SIZE);
	buffer[0]|=0x1;//first block for root directory
	last_bytes=free_block_bits_cnt/8;
	last_bits=free_block_bits_cnt%8;
	last_size=sb.block_bitmap_sec*SECTOR_SIZE-last_bytes;
	memset(&buffer[last_bytes],0xff,last_size);
	while(bits_index<last_bits){
		buffer[last_bytes]&=~(1<<bits_index);
		bits_index++;
	}
	//write block bitmap
	ide_write(p->disk,buffer,sb.block_bitmap_lba,sb.block_bitmap_sec);

	memset(buffer,0,SECTOR_SIZE);
	buffer[0]|=0x01;//first inode for root
	//write inode bitmap in disk
	ide_write(p->disk,buffer,sb.inode_bitmap_lba,sb.inode_bitmap_sec);
	
	memset(buffer,0,SECTOR_SIZE);
	struct inode* root_inode=(struct inode*)buffer;	
	root_inode->i_no=0;
	root_inode->i_size=2*sizeof(struct dir_entry);
	root_inode->blocks[0]=sb.free_block_lba;
	ide_write(p->disk,buffer,sb.inode_table_lba,1);	
	
	//write root to disk	
	memset(buffer,0,SECTOR_SIZE);	
	struct dir_entry* pe;
	pe=(struct dir_entry*)buffer;
	create_dir_entry(".",0,FT_DIR,pe);
	create_dir_entry("..",0,FT_DIR,pe+1);	
	ide_write(p->disk,buffer,sb.free_block_lba,1);	
	sys_free(buffer);
}

void partition_format(struct partition* p,bool force){
	struct super_block sb;
	ide_read(p->disk,(char*)&sb,p->start_lba+1,1);
	if(force || sb.magic!=FS_MAGIC){
		_partition_format(p);	
	}else{
		printk("%s has format skip\n",p->name);
	}
}


static bool mount(struct partition* part){
	struct super_block* sb;	
	//load super block
	list_init(&part->open_inode_list);
	sb=(struct super_block*)sys_malloc(SECTOR_SIZE);
	ASSERT(sb);
	part->sb=sb;
	ide_read(part->disk,(char*)sb,part->start_lba+1,1);
	
	//load block bitmap
	part->block_bitmap.bits=(uint8_t*)sys_malloc(part->sb->block_bitmap_sec*SECTOR_SIZE);
	ASSERT(part->block_bitmap.bits);
	part->block_bitmap.bmap_bytes_len=part->sb->block_bitmap_sec*SECTOR_SIZE;
	bitmap_init(&part->block_bitmap);
	ide_read(part->disk,(char*)part->block_bitmap.bits \
		,part->sb->block_bitmap_lba,part->sb->block_bitmap_sec);	

	//load inode bitmap
	part->inode_bitmap.bits=(uint8_t*)sys_malloc(SECTOR_SIZE);
	ASSERT(part->inode_bitmap.bits);
	part->inode_bitmap.bmap_bytes_len=SECTOR_SIZE;
	bitmap_init(&part->inode_bitmap);
	ide_read(part->disk,(char*)part->inode_bitmap.bits,part->sb->inode_bitmap_lba,1);
	curr_part=part;
	//open root dir
	open_root_dir(part);
	return true;

}

static bool find_mount_part(list_elem* tag,int arg){
	const char* target=(const char*)arg;
	struct partition* part;
	part=elem2entry(struct partition,tag,tag);
	if(!strcmp(part->name,target)){
		if(mount(part)){
			printk("mount %s ok free lba %x\n",part->name,part->sb->free_block_lba);
		}else{
			printk("mount failed");
		}
		return false;
	}
	return true;
}

void mount_partition(const char* part_name){
	list_traversal(&partition_list,find_mount_part,(int)part_name);
}
